package com.example.demo.core.base;

import cn.hutool.core.util.StrUtil;
import com.example.demo.core.page.PageResult;
import com.example.demo.core.page.QueryCondition;
import com.example.demo.core.util.JpaUtils;
import com.example.demo.core.util.SqlUtils;
import com.google.common.collect.Lists;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.Predicate;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class BaseService<T extends BaseEntity, ID extends Long> {

    private final BaseRepository<T, ID> baseRepository;

    public BaseService(BaseRepository<T, ID> baseRepository) {
        this.baseRepository = baseRepository;
    }

    @SuppressWarnings(value = "all")
    public T save(T entity) {
        if (entity != null && entity.getId() != null) {
            baseRepository.findById((ID) entity.getId()).ifPresent(source -> JpaUtils.copyNotNullProperties(source, entity));
        }
        return baseRepository.save(entity);
    }

    @SuppressWarnings(value = "all")
    public List<T> saveAll(Collection<T> collection) {
        collection.forEach(entity -> {
            if (entity != null && entity.getId() != null) {
                baseRepository.findById((ID) entity.getId()).ifPresent(source -> JpaUtils.copyNotNullProperties(source, entity));
            }
        });
        return baseRepository.saveAll(collection);
    }

    public void deleteById(ID id) {
        baseRepository.deleteById(id);
    }

    public void deleteAll() {
        baseRepository.deleteAll();
    }

    public T findById(ID id) {
        return baseRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("id不存在"));
    }

    public List<T> findAll() {
        return baseRepository.findAll();
    }

    public PageResult<T> findPage(QueryCondition condition) {
        Sort sort = Sort.by(condition.getOrderMap().entrySet().stream().map(entry -> new Sort.Order(StrUtil.isNotBlank(entry.getValue()) ? Sort.Direction.fromString(entry.getValue()) : Sort.Direction.ASC, entry.getKey())).collect(Collectors.toList()));
        Pageable pageable = PageRequest.of(condition.getPageNum() - 1, condition.getPageSize(), sort);
        Specification<T> specification = (root, query, cb) -> {
            List<Predicate> predicates = Lists.newArrayList();
            condition.getWhereList().stream()
                    .filter(attr -> StrUtil.isNotBlank(attr.getKey()) && StrUtil.isNotBlank(attr.getVal()))
                    .forEach(attr -> {
                        String opt = StrUtil.isNotBlank(attr.getOpt()) ? attr.getOpt().toLowerCase() : "eq";
                        switch (opt) {
                            case "eq":
                                predicates.add(cb.equal(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "like":
                                predicates.add(cb.like(root.get(attr.getKey()), "%" + SqlUtils.trimAndEscape(attr.getVal()) + "%"));
                                break;
                            case "gt":
                                predicates.add(cb.greaterThan(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "lt":
                                predicates.add(cb.lessThan(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "gte":
                                predicates.add(cb.greaterThanOrEqualTo(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "lte":
                                predicates.add(cb.lessThanOrEqualTo(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "in":
                                predicates.add(root.get(attr.getKey()).in(StrUtil.split(attr.getVal(), ",")));
                                break;
                            case "notIn":
                                predicates.add(cb.not(root.get(attr.getKey()).in(StrUtil.split(attr.getVal(), ","))));
                                break;
                            case "neq":
                                predicates.add(cb.notEqual(root.get(attr.getKey()), attr.getVal()));
                                break;
                            case "null":
                                predicates.add(cb.isNull(root.get(attr.getKey())));
                                break;
                            case "notNull":
                                predicates.add(cb.isNotNull(root.get(attr.getKey())));
                                break;
                            default:
                                throw new IllegalArgumentException("不支持的查询方式");
                        }
                    });
            if (!predicates.isEmpty()) {
                return cb.and(predicates.toArray(new Predicate[0]));
            }
            return cb.conjunction();
        };
        Page<T> page = baseRepository.findAll(specification, pageable);
        return PageResult.ok(page);
    }
}
